package middleware

import (
	"gin-blog/utils"
	"gin-blog/utils/msg"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"time"
)

// JwtKey 密钥
var JwtKey = []byte(utils.JwtKey)

type MyClaims struct {
	// 这变量名应该与User中一致，因为是使用Username作为校验
	Username string `json:"username"`
	// jwt自带变量
	jwt.StandardClaims
}

// SetToken 生成token
func SetToken(username string) (string, int) {
	// 设置token有效时间
	expireTime := time.Now().Add(10 * time.Hour)
	SetClaims := MyClaims{
		username,
		jwt.StandardClaims{
			// 设置过期时间
			ExpiresAt: expireTime.Unix(),
			// 设置签发者
			Issuer: "blog",
		},
	}
	// 根据指定的算法生成 jwt token
	reqClaim := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims)
	// 根据key获取完整的签名令牌
	token, err := reqClaim.SignedString(JwtKey)
	if err != nil {
		return "", msg.ERROR_TOKEN_CREATE
	}
	return token, msg.SUCCESS
}

// CheckToken 验证token
func CheckToken(token string) (*MyClaims, int) {
	// ParseWithClaims 方法用于校验鉴权的声明用于解析鉴权的声明
	setToken, _ := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return JwtKey, nil
	})
	// Valid 方法用于校验鉴权的声明
	if key, _ := setToken.Claims.(*MyClaims); setToken.Valid {
		return key, msg.SUCCESS
	} else {
		return nil, msg.ERROR_TOKEN_WRONG
	}
}

// JwtToken jwt中间件
func JwtToken() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenHeader := c.Request.Header.Get("Authorization")
		code := msg.SUCCESS
		if tokenHeader == "" {
			code = msg.ERROR_TOKEN_EXIST
			c.JSON(http.StatusOK, gin.H{
				"code": code,
				"msg":  msg.GetErrorMsg(code),
			})
			// 中止链的调用，阻止后续操作进行
			c.Abort()
			return
		}
		// 格式验证，固定写法
		checkToken := strings.SplitN(tokenHeader, " ", 2)
		if len(checkToken) != 2 && checkToken[0] != "Bearer" {
			code = msg.ERROR_TOKEN_TYPE_WRONG
			c.JSON(http.StatusOK, gin.H{
				"code": code,
				"msg":  msg.GetErrorMsg(code),
			})
			c.Abort()
			return
		}
		// token验证
		key, checkCode := CheckToken(checkToken[1])
		if checkCode == msg.ERROR {
			code = msg.ERROR_TOKEN_WRONG
			c.JSON(http.StatusOK, gin.H{
				"code": code,
				"msg":  msg.GetErrorMsg(code),
			})
			c.Abort()
			return
		}
		// 超时验证
		if time.Now().Unix() > key.ExpiresAt {
			code = msg.ERROR_TOKEN_TIMEOUT
			c.JSON(http.StatusOK, gin.H{
				"code": code,
				"msg":  msg.GetErrorMsg(code),
			})
			c.Abort()
			return
		}
		c.Set("username", key.Username)
		// 进行后续操作
		c.Next()
	}
}
